package cn.zf233.xcloud.util;

import cn.zf233.xcloud.commom.Const;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.MediaType;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Created by zf233 on 2021/1/12
 * OSS 工具类
 */
public class OSSUtil {

    private final String endpoint;
    private final String accessKeyId;
    private final String accessKeySecret;
    private final String bucketName;

    volatile private static OSS ossClient;

    public static OSS getOSSClientSingleton() {
        if (ossClient == null) {
            synchronized (OSSUtil.class) {
                if (ossClient == null) {
                    ossClient = new OSSClientBuilder().build("endpoint", "accessKeyId", "accessKeySecret");
                }
            }
        }
        return ossClient;
    }

    public OSS getOSSClient() {
        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }

    public URL getPermissionUrl(String objectName, Long timeout, TimeUnit unit) {
        OSS ossClient = ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        if (objectNameExists(ossClient, objectName)) {
            Date expiration = new Date(ThreadSafeDateUtil.now() + unit.toMillis(timeout));
            URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
            ossClient.shutdown();
            return url;
        }
        ossClient.shutdown();
        return null;
    }

    // 字节数组上传
    public void upload(OSS ossClient, String randomName, String oldName, byte[] content) throws IOException {
        this.upload(ossClient, randomName, oldName, new ByteArrayInputStream(content));
    }

    // 流上传
    public void upload(OSS ossClient, String randomName, String oldName, InputStream inputStream) throws IOException {
        String encodeOldName;
        try {
            // oldName encode
            encodeOldName = URLEncoder.encode(oldName, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            // oldName encode fail
            encodeOldName = randomName;
        }
        // Encoding failed to persist with a random name
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, randomName, inputStream);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentDisposition("attachment;filename=" + encodeOldName);
        metadata.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        metadata.setContentLength(inputStream.available());
        putObjectRequest.setMetadata(metadata);
        ossClient.putObject(putObjectRequest);
    }

    public String initShard(OSS ossClient, String randomName, String oldName, Long size) {
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, randomName);
        String encodeOldName;
        try {
            // oldName encode
            encodeOldName = URLEncoder.encode(oldName, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            // oldName encode fail
            encodeOldName = randomName;
        }
        // Encoding failed to persist with a random name
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentDisposition("attachment;filename=" + encodeOldName);
        metadata.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        metadata.setContentLength(size);
        request.setObjectMetadata(metadata);
        InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
        return upresult.getUploadId();
    }

    public PartETag uploadShard(OSS ossClient, String randomName, String uploadId, byte[] bytes, Integer shardId) {
        UploadPartRequest uploadPartRequest = new UploadPartRequest();
        uploadPartRequest.setBucketName(bucketName);
        uploadPartRequest.setKey(randomName);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setInputStream(new ByteArrayInputStream(bytes));
        uploadPartRequest.setPartSize(bytes.length);
        uploadPartRequest.setPartNumber(shardId);
        UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
        return uploadPartResult.getPartETag();
    }

    public void submitShard(OSS ossClient, String radomName, String uploadId, List<PartETag> partETagList) {
        CompleteMultipartUploadRequest completeMultipartUploadRequest =
                new CompleteMultipartUploadRequest(bucketName, radomName, uploadId, partETagList);
        ossClient.completeMultipartUpload(completeMultipartUploadRequest);
    }

    public void cancelShard(OSS ossClient, String radomName, String uploadId) {
        AbortMultipartUploadRequest abortMultipartUploadRequest =
                new AbortMultipartUploadRequest(bucketName, radomName, uploadId);
        ossClient.abortMultipartUpload(abortMultipartUploadRequest);
    }

    public void rename(OSS ossClient, String randomName, String newName, Long fileSize) {
        CopyObjectRequest request = new CopyObjectRequest(bucketName, randomName, bucketName, randomName);
        String encodeNewName;
        try {
            encodeNewName = URLEncoder.encode(newName, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            encodeNewName = randomName;
        }
        ObjectMetadata newMetadata = new ObjectMetadata();
        newMetadata.setContentDisposition("attachment;filename=" + encodeNewName);
        newMetadata.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        newMetadata.setContentLength(fileSize);
        request.setNewObjectMetadata(newMetadata);

        ossClient.copyObject(request);
    }

    public void copy(OSS ossClient, String randomName, String copyRandomName) {
        CopyObjectRequest request = new CopyObjectRequest(bucketName, randomName, bucketName, copyRandomName);
        ossClient.copyObject(request);
    }

    public Boolean objectNameExists(OSS ossClient, String objectName) {
        return ossClient.doesObjectExist(bucketName, objectName);
    }

    public void delete(OSS ossClient, String objectName) {
        ossClient.deleteObject(bucketName, objectName);
    }

    public void deleteBatch(OSS ossClient, List<String> objectNames) {
        ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(objectNames));
    }

    public void bucketInfo() {
        OSS ossClient = getOSSClient();
        BucketInfo info = ossClient.getBucketInfo(bucketName);
    }

    public String getRandomFileName(String fileName) {
        return StringUtils.reverse(String.valueOf(System.currentTimeMillis())) +
                Const.DOMAIN_NAME +
                UUID.randomUUID().toString().replace("-", "") +
                fileName.substring(fileName.lastIndexOf("."));
    }

    ;
    public OSSUtil(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
        this.endpoint = endpoint;
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
        this.bucketName = bucketName;
    }
}
